package com.sky.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Map;

public class JwtUtil {
    /**
     * 生成jwt
     * 使用Hs256算法, 私匙使用固定秘钥
     *
     * @param secretKey jwt秘钥
     * @param ttlMillis jwt过期时间(毫秒)
     * @param claims    设置的信息
     * @return
     */
    public static String createJWT(String secretKey, long ttlMillis, Map<String, Object> claims) {
        // 指定签名的时候使用的签名算法，也就是header那部分
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

        //secretKey密钥
        // 生成JWT的时间
        long expMillis = System.currentTimeMillis() + ttlMillis;
        Date exp = new Date(expMillis);//通过当前时间(毫秒数,从 1970-01-01 00:00:00 UTC 开始计算)加上 ttlMillis 得到 JWT 的过期时间 exp。
        //JWT 的过期时间（exp声明）需要是Date类型（或符合规范的时间戳），这一步是为了满足库的参数要求。
        //这个exp会被包含在 JWT 的Payload（载荷）中，作为标准声明之一，用于告知验证方 “令牌何时失效”。

        // 设置jwt的body
        //细节说明：JwtBuilder是构建 JWT 的核心工具类，提供了一系列方法用于设置头部、载荷、签名等信息。
        JwtBuilder builder = Jwts.builder()
                // 如果有私有声明，一定要先设置这个自己创建的私有的声明，这个是给builder的claim赋值，一旦写在标准的声明赋值之后，就是覆盖了那些标准的声明的
                .setClaims(claims)
                // 设置签名使用的签名算法和签名使用的秘钥。
                // 因为加密算法（如 HMAC-SHA256）的计算需要基于 “字节数据”，而不是字符串，所以必须做这一步转换（避免不同编码格式导致签名计算错误）。
                .signWith(signatureAlgorithm, secretKey.getBytes(StandardCharsets.UTF_8))
                // 设置过期时间
                .setExpiration(exp);

        return builder.compact();//将构建好的 JWT 各个部分（Header、Payload、Signature）压缩并拼接成一个完整的字符串
    }
    //compact首先对 Header 和 Payload 进行 Base64URL 编码（这一过程并非加密，仅用于数据的压缩和便于传输），
    // 指定的算法和密钥，对 “编码后的 Header. 编码后的 Payload” 这一组合字符串进行签名以生成 Signature，
    // 最后将编码后的 Header、编码后的 Payload 和生成的 Signature 三部分用英文句号 “.” 连接起来，形成格式为 “Header.Payload.Signature” 的最终 JWT 字符串，
    // 该字符串可用于客户端与服务端之间的传输（例如放在 HTTP 请求的 Header 中）。
    /**
     * Token解密
     *
     * @param secretKey jwt秘钥 此秘钥一定要保留好在服务端, 不能暴露出去, 否则sign就可以被伪造, 如果对接多个客户端建议改造成多个
     * @param token     加密后的token
     * @return
     */
    public static Claims parseJWT(String secretKey, String token) {
        // 得到DefaultJwtParser
        Claims claims = Jwts.parser()
                // 设置签名的秘钥
                .setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8))
                // 设置需要解析的jwt
                .parseClaimsJws(token).getBody();
        return claims;
    }

}
